using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Devv.Core.Erroo;
using Devv.Core.Utils;
using Devv.Threeter.support.devv.com;
using Devv.Threeter.Twitter;
using TweetSharp;

namespace Devv.Threeter
{
	public partial class FormPost : Form
	{
		#region Constants

		private const Int32 DEFAULT_WIDTH = 600;
		private const Int32 DEFAULT_HEIGHT = 400;
		private const Int32 MINI_WIDTH = 240;
		private const Int32 MINI_HEIGHT = 130;

		#endregion

		#region Variables

		// Please change here to use your own key and secret from Twitter!!!
		private readonly TwitterService twitter = new TwitterService(ConfigUtil.GetConfig("Twitter.ConsumerKey"), ConfigUtil.GetConfig("Twitter.ConsumerSecret"));

		private String MessageText { get; set; }
		private Keys LastPressedKey { get; set; }
		private Int32 LastWidth { get; set; }
		private Int32 LastHeight { get; set; }
		private Boolean CloseForm { get; set; }

		public Boolean MiniMode
		{
			get
			{
				return Properties.Settings.Default.MiniMode;
			}
			set
			{
				SetMiniMode(value);
			}
		}

		public Boolean EnableFriendsUpdates
		{
			get
			{
				return Properties.Settings.Default.EnableFriendsUpdates;
			}
			set
			{
				menuTray_FriendsUpdates.Checked = value;
			}
		}

		#endregion

		#region Main methods and loading

		public FormPost()
		{
			InitializeComponent();

			Icon = Properties.Resources.Threeter;
			iconTray.Icon = Properties.Resources.Threeter;

			MessageText = String.Empty;
			lblCharCount.Text = TwitterManager.MAXCHARACTERS.ToString();

			SetMiniMode(Properties.Settings.Default.MiniMode);

			if (Program.CurrentAccounts.Count > 0)
			{
				Account account = Program.CurrentAccounts.Find(Properties.Settings.Default.Username);

				if (account != null)
				{
					TwitterManager.LoggedAccount = account;
				}
				else
				{
					TwitterManager.LoggedAccount = Program.CurrentAccounts[0];
				}

				RefreshMenuAccounts();
			}

			timerGetUpdates.Start();
			timerErrorCheck.Start();

			if (Program.AutoRun)
			{
				WindowState = FormWindowState.Minimized;
			}
		}

		private void FormPost_Load(Object sender, EventArgs e)
		{
			CheckAccount(true);

			if (!String.IsNullOrEmpty(Properties.Settings.Default.HotKey))
			{
				SetHotKey(Properties.Settings.Default.HotKey);
			}

			if (Properties.Settings.Default.FirstRun)
			{
				Properties.Settings.Default.FirstRun = false;

				if (Properties.Settings.Default.EnableHotKey)
				{
					ShowTrayBaloon(Messages.StartBaloonTextHotKey.Replace("#", Properties.Settings.Default.HotKey), GlobalMessages.AppName, String.Empty, ToolTipIcon.Info);
				}
				else
				{
					ShowTrayBaloon(Messages.StartBaloonText, String.Empty, GlobalMessages.AppName, ToolTipIcon.Info);
				}
			}

			menuTray_FriendsUpdates.Checked = Properties.Settings.Default.EnableFriendsUpdates;
		}

		private void FormPost_KeyDown(Object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Escape)
			{
				HideWindow();
			}
		}

		private void FormPost_MouseClick(Object sender, MouseEventArgs e)
		{
			if (!txtMessage.Enabled && !workerUpdateStatus.IsBusy)
			{
				Program.ShowFormAccounts(this);
			}
		}

		private void FormPost_HelpRequested(Object sender, HelpEventArgs hlpevent)
		{
			Process.Start(GlobalMessages.AppUrl);
			hlpevent.Handled = true;
		}

		private void FormPost_Activated(Object sender, EventArgs e)
		{
			CheckAccount(false);
			SetCharCounter();

			if (txtMessage.Enabled && txtMessage.Visible)
			{
				txtMessage.Focus();
			}
			else if (txtMessageMini.Enabled && txtMessageMini.Visible)
			{
				txtMessageMini.Focus();
			}
		}

		private void FormPost_ResizeEnd(Object sender, EventArgs e)
		{
			if (!txtMessageMini.Visible)
			{
				LastWidth = Width;
				LastHeight = Height;
			}
		}

		[SecurityCritical]
		private void FormPost_FormClosing(Object sender, FormClosingEventArgs e)
		{
			if (!CloseForm && e.CloseReason == CloseReason.UserClosing)
			{
				e.Cancel = true;
				HideWindow();
			}
			else
			{
				if (Properties.Settings.Default.FirstRun)
				{
					MaintenanceManager.Cleanup();
					Properties.Settings.Default.FirstRun = false;
				}

				Properties.Settings.Default.Width = LastWidth;
				Properties.Settings.Default.Height = LastHeight;
				Properties.Settings.Default.Username = TwitterManager.LoggedAccount.Username;

				Properties.Settings.Default.Save();

				Program.CurrentAccounts.Save();

				try
				{
					UnregisterHotKey(Handle, 31717);
				}
				catch (Exception ex)
				{
					ErrorHandler.Handle(ex);
				}
			}
		}

		#endregion

		#region Visual effects, show and hide window

		private void FadeOutWindow()
		{
			Opacity -= 0.02;

			if (Opacity <= 0)
			{
				HideWindow();
			}
			else
			{
				Thread.Sleep(5);
				Application.DoEvents();
				FadeOutWindow();
			}
		}

		private void ShowWindow()
		{
			Opacity = 0.86;

			TopMost = true;

			Show();
			BringToFront();
			Activate();
			WindowState = FormWindowState.Normal;

			txtMessage.Focus();
		}

		private void HideWindow()
		{
			Hide();
			WindowState = FormWindowState.Minimized;
		}

		private void SetMiniMode(Boolean mini)
		{
			Properties.Settings.Default.MiniMode = mini;

			if (mini)
			{
				txtMessageMini.Visible = true;
				txtMessageMini.BringToFront();

				FormBorderStyle = FormBorderStyle.FixedToolWindow;
				Width = MINI_WIDTH;
				Height = MINI_HEIGHT;
				Location = new Point(Screen.PrimaryScreen.Bounds.Width - Size.Width - SystemInformation.FrameBorderSize.Width, Screen.PrimaryScreen.WorkingArea.Height - Size.Height - SystemInformation.FrameBorderSize.Height);

				txtMessageMini.Text = MessageText;
				menuTray_MiniMode.Checked = true;
			}
			else
			{
				txtMessageMini.Visible = false;
				txtMessageMini.SendToBack();

				FormBorderStyle = FormBorderStyle.Sizable;
				Width = DEFAULT_WIDTH;
				Height = DEFAULT_HEIGHT;
				Location = new Point(Screen.PrimaryScreen.Bounds.Width / 2 - Size.Width / 2 - SystemInformation.FrameBorderSize.Width, Screen.PrimaryScreen.WorkingArea.Height / 2 - Size.Height / 2 - SystemInformation.FrameBorderSize.Height);

				txtMessage.Text = MessageText;
				menuTray_MiniMode.Checked = false;
			}
		}

		#endregion

		#region Tray icon methods

		private void menuTray_UpdateStatus_Click(Object sender, EventArgs e)
		{
			ShowWindow();
		}

		private void menuTray_AddNewAccount_Click(Object sender, EventArgs e)
		{
			Program.ShowFormAccounts(this);
		}

		private void menuTray_SelectAccount(Object sender, EventArgs e)
		{
			ToolStripMenuItem item = (ToolStripMenuItem)sender;
			Account login = Program.CurrentAccounts.Find(item.Text);

			SetCurrentAccount(login);
		}

		private void menuTray_ViewTimeline_Click(Object sender, EventArgs e)
		{
			Program.ShowFriendUpdates();
		}

		private void menuTray_FriendsUpdates_CheckedChanged(Object sender, EventArgs e)
		{
			Properties.Settings.Default.EnableFriendsUpdates = menuTray_FriendsUpdates.Checked;
		}

		private void menuTray_MiniMode_CheckedChanged(Object sender, EventArgs e)
		{
			Properties.Settings.Default.MiniMode = menuTray_MiniMode.Checked;

			MiniMode = menuTray_MiniMode.Checked;
		}

		private void menuTray_Options_Click(Object sender, EventArgs e)
		{
			Program.ShowFormOptions(this, false);
		}

		private void menuTray_Donate_Click(Object sender, EventArgs e)
		{
			Process.Start(GlobalMessages.DonateUrl);
		}

		private void menuTray_Exit_Click(Object sender, EventArgs e)
		{
			CloseForm = true;
			Application.Exit();
		}

		private void iconTray_MouseClick(Object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left)
			{
				ShowWindow();
			}
		}

		private void iconTray_MouseDoubleClick(Object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left)
			{
				ShowWindow();
			}
		}

		private void iconTray_BalloonTipClicked(Object sender, EventArgs e)
		{
			if (iconTray.Tag != null)
			{
				String tag = iconTray.Tag.ToString();

				if (tag == "timeline")
				{
					Program.ShowFriendUpdates();
				}
				else if (tag == "download")
				{
					MaintenanceManager.DownloadLatest();
				}
			}
		}

		private void iconTray_BalloonTipClosed(Object sender, EventArgs e)
		{
			iconTray.Tag = null;
		}

		public void ShowTrayBaloon(String text, String title, String tag, ToolTipIcon icon)
		{
			if (String.IsNullOrWhiteSpace(text))
			{
				return;
			}

			Int32 timeout = 14000;

			if (text.Length < 50)
			{
				timeout = 6000;
			}
			else if (text.Length < 120)
			{
				timeout = 10000;
			}

			iconTray.ShowBalloonTip(timeout, title, text, icon);
			iconTray.Tag = tag;
		}

		public void RefreshMenuAccounts()
		{
			for (Int32 i = menuTray_Accounts.DropDownItems.Count - 2; i >= 0; i--)
			{
				menuTray_Accounts.DropDownItems.RemoveAt(i);
			}

			for (Int32 i = Program.CurrentAccounts.Count - 1; i >= 0; i--)
			{
				ToolStripMenuItem item = new ToolStripMenuItem();

				item.Click += menuTray_SelectAccount;
				item.Text = Program.CurrentAccounts[i].Username;

				if (Program.CurrentAccounts[i].Username == TwitterManager.LoggedAccount.Username)
				{
					item.Checked = true;
				}

				menuTray_Accounts.DropDownItems.Insert(0, item);
			}

			CheckAccount(false);
		}

		#endregion

		#region Account stuff

		public void SetCurrentAccount(Account login)
		{
			TwitterManager.LoggedAccount = login;
			Properties.Settings.Default.Username = login.Username;

			Text = GlobalMessages.AppName + " (" + login.Username + ")";

			RefreshMenuAccounts();
		}

		private void CheckAccount(Boolean show)
		{
			if (Program.CurrentAccounts.Count < 1 || String.IsNullOrEmpty(TwitterManager.LoggedAccount.Username))
			{
				Icon = Properties.Resources.ThreeterDisabled;
				iconTray.Icon = Properties.Resources.ThreeterDisabled;

				txtMessage.Text = Messages.LoginNotSet;
				txtMessage.Enabled = false;
				txtMessageMini.Enabled = false;
				butUpdateStatus.Enabled = false;
				menuTray_UpdateStatus.Enabled = false;
				menuTray_ViewTimeline.Enabled = false;

				if (show)
				{
					MessageBox.Show(Messages.LoginNotSet, Messages.Login, MessageBoxButtons.OK, MessageBoxIcon.Information);
					Program.ShowFormAccounts(this);
				}
			}
			else
			{
				Icon = Properties.Resources.Threeter;
				iconTray.Icon = Properties.Resources.Threeter;

				if (txtMessage.Text.Equals(Messages.LoginNotSet))
				{
					txtMessage.Text = String.Empty;
				}

				txtMessage.Enabled = true;
				txtMessageMini.Enabled = true;
				butUpdateStatus.Enabled = true;
				menuTray_UpdateStatus.Enabled = true;
				menuTray_ViewTimeline.Enabled = true;

				Text = GlobalMessages.AppName + " (" + TwitterManager.LoggedAccount.Username + ")";
			}
		}

		#endregion

		#region Message posting

		private void txtMessage_KeyDown(Object sender, KeyEventArgs e)
		{
			Boolean update = false;

			if (Properties.Settings.Default.EnterKeyPosts || LastPressedKey == Keys.Enter)
			{
				update = true;
			}

			LastPressedKey = e.KeyCode;

			if (e.KeyCode == Keys.Escape)
			{
				HideWindow();
			}
			else if (e.KeyCode == Keys.Enter && e.Modifiers != Keys.Control && update)
			{
				UpdateStatus();

				e.Handled = true;
				e.SuppressKeyPress = true;
			}
			else
			{
				timerShortenUrl.Enabled = true;
				timerShortenUrl.Start();
			}
		}

		private void txtMessage_TextChanged(Object sender, EventArgs e)
		{
			MessageText = ((TextBox)sender).Text;

			SetCharCounter();
		}

		private void butUpdateStatus_Click(Object sender, EventArgs e)
		{
			UpdateStatus();
		}

		private void workerUpdateStatus_DoWork(Object sender, DoWorkEventArgs e)
		{
			String message = e.Argument.ToString();

			if (Properties.Settings.Default.ShortenUrl)
			{
				message = ShortUrlManager.Parse(message);
			}

			try
			{
				TwitterManager.UpdateStatus(message);

				e.Result = String.Empty;
			}
			catch (Exception ex)
			{
				ErrorHandler.Handle(ex);

				e.Result = ex.ToString();
			}
		}

		private void workerUpdateStatus_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
		{
			String msg = e.Result.ToString();

			imgLoading.Visible = false;
			txtMessage.Visible = true;
			butUpdateStatus.Enabled = true;

			Application.DoEvents();

			if (String.IsNullOrEmpty(msg))
			{
				txtMessage.Text = String.Empty;

				FadeOutWindow();
			}
			else if (msg.Contains("401"))
			{
				MessageBox.Show(Messages.ErrorInvalidLogin, Messages.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);

				Program.ShowFormAccounts(this);
			}
			else
			{
				MessageBox.Show(Messages.ErrorStatusUpdate + Environment.NewLine, Messages.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void UpdateStatus()
		{
			if (String.IsNullOrEmpty(MessageText))
			{
				return;
			}

			MessageText = MessageText.Trim();

			if (!NetworkInterface.GetIsNetworkAvailable())
			{
				MessageBox.Show(Messages.Offline + "!", Messages.Offline, MessageBoxButtons.OK, MessageBoxIcon.Information);
				return;
			}

			if (MessageText.Length > TwitterManager.MAXCHARACTERS)
			{
				MessageBox.Show(Messages.ErrorMessageLength.Replace("#", TwitterManager.MAXCHARACTERS.ToString()), Messages.Error, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}

			while (workerUpdateStatus.IsBusy || workerShortenUrl.IsBusy)
			{
				Thread.Sleep(50);
				Application.DoEvents();
			}

			butUpdateStatus.Enabled = false;
			txtMessage.Visible = false;
			imgLoading.Visible = true;

			TopMost = false;

			workerUpdateStatus.RunWorkerAsync(MessageText);
		}

		private void SetCharCounter()
		{
			Font font = lblCharCount.Font;
			Int32 count = TwitterManager.MAXCHARACTERS - MessageText.Length;

			lblCharCount.Text = count.ToString();

			if (count < 10)
			{
				lblCharCount.Font = new Font(font.FontFamily, font.Size, FontStyle.Bold);
			}
			else
			{
				lblCharCount.Font = new Font(font.FontFamily, font.Size, FontStyle.Regular);
			}

			if (count < 1)
			{
				lblCharCount.ForeColor = Color.Red;
			}
			else
			{
				lblCharCount.ForeColor = SystemColors.ControlText;
			}

			lblRemaining.Font = lblCharCount.Font;
			lblRemaining.ForeColor = lblCharCount.ForeColor;
		}

		#endregion

		#region Shorten URLs

		private void timerShortenUrl_Tick(Object sender, EventArgs e)
		{
			timerShortenUrl.Stop();

			if (!workerShortenUrl.IsBusy)
			{
				imgShortenUrl.Visible = true;
				workerShortenUrl.RunWorkerAsync(MessageText);
			}
		}

		private void workerShortenUrl_DoWork(Object sender, DoWorkEventArgs e)
		{
			try
			{
				e.Result = ShortUrlManager.Parse(e.Argument.ToString());
			}
			catch
			{
				e.Result = null;
			}
		}

		private void workerShortenUrl_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Result != null && !timerShortenUrl.Enabled)
			{
				if (MiniMode)
				{
					txtMessageMini.Text = e.Result.ToString();
				}
				else
				{
					txtMessage.Text = e.Result.ToString();
				}
			}

			imgShortenUrl.Visible = false;
		}

		#endregion

		#region Friends updates

		private void timerGetUpdates_Tick(Object sender, EventArgs e)
		{
			timerGetUpdates.Stop();
			timerGetUpdates.Interval = 280000;

			if (!String.IsNullOrEmpty(TwitterManager.LoggedAccount.Username) && Properties.Settings.Default.EnableFriendsUpdates && NetworkInterface.GetIsNetworkAvailable())
			{
				workerGetUpdates.RunWorkerAsync(Properties.Settings.Default.LastStatusId);
			}
			else
			{
				timerGetUpdates.Start();
			}
		}

		private void workerGetUpdates_DoWork(Object sender, DoWorkEventArgs e)
		{
			Int64 sinceId = (Int64)e.Argument;

			try
			{
				e.Result = TwitterManager.GetTimeline(sinceId);
			}
			catch (Exception ex)
			{
				ErrorHandler.Handle(ex);
			}
		}

		private void workerGetUpdates_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Result == null)
			{
				return;
			}

			ShowFriendsUpdates((IEnumerable<TwitterStatus>)e.Result);

			timerGetUpdates.Start();
		}

		public void ShowFriendsUpdates(IEnumerable<TwitterStatus> timeline)
		{
			if (timeline == null)
			{
				return;
			}

			StringBuilder msg = new StringBuilder();
			String lastUser = String.Empty;
			Int32 max = 3;
			Int32 counter = 0;

			foreach (TwitterStatus status in timeline)
			{
				if (status.RawSource.Contains("<error>"))
				{
					MessageBox.Show(Messages.ErrorInvalidLogin, Messages.Error, MessageBoxButtons.OK, MessageBoxIcon.Warning);
					Program.ShowFormAccounts(this);

					return;
				}

				Properties.Settings.Default.LastStatusId = status.Id;

				if (status.User == null || lastUser.Equals(status.User.ScreenName))
				{
					continue;
				}

				if (counter > max)
				{
					counter++;
					continue;
				}

				lastUser = status.User.ScreenName;

				String text = status.Text;

				if (text.Length > 103)
				{
					text = text.Substring(0, 100) + "...";
				}

				msg.Append(Environment.NewLine);
				msg.Append(status.User.ScreenName + ": " + text);
				msg.Append(Environment.NewLine);

				counter++;
			}

			if (counter > max)
			{
				msg.Append(Environment.NewLine);
				msg.Append(Messages.AndMoreUpdates.Replace("#", (counter - max).ToString()));
			}

			Properties.Settings.Default.Save();

			ShowTrayBaloon(msg.ToString(), Messages.FriendsUpdates + " (" + counter + ")", "timeline", ToolTipIcon.Info);
		}

		#endregion

		#region Hotkey methods

		[SecuritySafeCritical]
		public void SetHotKey(String hotkey)
		{
			hotkey = hotkey.Replace(" ", String.Empty);

			KeyModifiers modifiers = KeyModifiers.None;
			Int32 index = hotkey.LastIndexOf(Convert.ToChar("+")) + 1;
			String key = hotkey.Substring(index, hotkey.Length - index);

			if (hotkey.Contains("ALT"))
			{
				modifiers |= KeyModifiers.Alt;
			}
			if (hotkey.Contains("CTRL"))
			{
				modifiers |= KeyModifiers.Control;
			}
			if (hotkey.Contains("SHIFT"))
			{
				modifiers |= KeyModifiers.Shift;
			}

			ClearHotKey();

			try
			{
				RegisterHotKey(Handle, 3173, modifiers, (Keys)Enum.Parse(typeof(Keys), key));
			}
			catch (Exception ex)
			{
				ErrorHandler.Handle(ex, Messages.ErrorRegistry);
			}
		}

		[SecuritySafeCritical]
		public void ClearHotKey()
		{
			try
			{
				UnregisterHotKey(Handle, 3173);
			}
			catch (Exception ex)
			{
				ErrorHandler.Handle(ex);
			}
		}

		[SecurityCritical]
		[DllImport("user32.dll", SetLastError = true)]
		protected static extern bool RegisterHotKey(IntPtr hWnd, Int32 id, KeyModifiers fsModifiers, Keys vk);

		[SecurityCritical]
		[DllImport("user32.dll", SetLastError = true)]
		protected static extern bool UnregisterHotKey(IntPtr hWnd, Int32 id);

		[SecuritySafeCritical]
		protected override void WndProc(ref Message m)
		{
			const int WM_HOTKEY = 0x0312;

			switch (m.Msg)
			{
				case WM_HOTKEY:
					ShowWindow();
					break;
			}

			base.WndProc(ref m);
		}

		[SecurityCritical]
		protected enum KeyModifiers
		{
			None = 0,
			Alt = 1,
			Control = 2,
			Shift = 4,
			Windows = 8
		}

		#endregion

		#region Error reporting

		private void timerErrorCheck_Tick(Object sender, EventArgs e)
		{
			timerErrorCheck.Stop();

			try
			{
				if (Directory.Exists(Config.LogPath) && Directory.GetFiles(Config.LogPath).Length > 10)
				{
					ThreadPool.QueueUserWorkItem(ReportErrors);

					timerErrorCheck.Interval = 3600000;
				}
				else
				{
					timerErrorCheck.Interval = 100000;
				}
			}
			catch (Exception ex)
			{
				ErrorHandler.Handle(ex);
			}

			timerErrorCheck.Start();
		}

		private static void ReportErrors(Object state)
		{
			if (!Properties.Settings.Default.ReportErrors || !Directory.Exists(Config.LogPath))
			{
				return;
			}

			String[] files = Directory.GetFiles(Config.LogPath, "*.htm");
			ReportErrors ws = new ReportErrors();
			StringBuilder sb = new StringBuilder();

			ws.Timeout = 180000;
			ws.EnableDecompression = true;

			for (Int32 i = 0; i < files.Length; i++)
			{
				try
				{
					sb.Append(IOUtil.ReadFile(files[i]));
					sb.Append("<hr />");

					File.Delete(files[i]);
				}
				catch
				{
					Thread.Sleep(200);

					try
					{
						File.Delete(files[i]);
					}
					catch
					{
						// Leave it...
					}
				}
			}

			try
			{
				Byte[] compressedHtml = CompressionUtil.GZipCompress(sb.ToString());
				ws.SendHtml("Threeter", TwitterManager.LoggedAccount.Username, compressedHtml);
			}
			catch
			{
				// Leave it...
			}
		}

		#endregion
	}
}